import os
import tempfile
import signal
import threading
import tkinter as tk
from tkinter import *

import PIL

import client.memory
from client.util.file import sep
from client.util.socket_listener import *
from tkinter.scrolledtext import ScrolledText
from tkinter import colorchooser
from tkinter import simpledialog
from tkinter import filedialog
from PIL import Image, ImageTk
from io import BytesIO
from client.util import socket_listener, file
import binascii
import shutil
from client import PhoneClient
import pickle
import struct
import threading
from server.event_handler import PhoneServer
from threading import Thread, current_thread
from signal import *
import time
import inspect
import ctypes
import argparse
import time
import sys
from chatAPP import Video_Server, Video_Client

import cv2
import PIL.Image
server = 100
clients = 2
isCreated = False
import socket

parser = argparse.ArgumentParser()

parser.add_argument('--host', type=str, default='127.0.0.1')
parser.add_argument('--port', type=int, default=8086)
parser.add_argument('--level', type=int, default=1)
parser.add_argument('-v', '--version', type=int, default=4)

args = parser.parse_args()

IP = args.host
PORT = args.port
VERSION = args.version
LEVEL = args.level

def _async_raise(tid, exctype):
    """raises the exception, performs cleanup if needed"""
    tid = ctypes.c_long(tid)
    if not inspect.isclass(exctype):
        exctype = type(exctype)
        print(1)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
    print(2)
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        # """if it returns a number greater than one, you're in trouble,
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")


def stop_thread(thread):
    _async_raise(thread.ident, SystemExit)


def phoneServerThread():
    print("test")
    global server
    server = PhoneServer.Server()
    server.start()
    print("server exit")


def clientServerThread():
    print("testClient")
    global isCreated
    isCreated = True
    global clients
    clients = PhoneClient.Client()
    clients.connect_to_server()
    clients.start_communication()
    print("client exit")


class ChatForm(tk.Frame):
    font_color = "#000000"
    font_size = 10
    user_list = []
    tag_i = 0
    image_labels = []
    open = 0

    thread1 = Thread(target=phoneServerThread, name='phoneServer')
    thread2 = Thread(target=clientServerThread, name='clientServer')
    isCalling = False

    def remove_listener_and_close(self):
        remove_message_listener(self.message_listener)
        client.util.socket_listener.remove_listener(self.socket_listener)
        self.master.destroy()
        if self.target['id'] in client.memory.window_instance[self.target['type']]:
            del client.memory.window_instance[self.target['type']][self.target['id']]

    def message_listener(self, data):
        self.receiving_message(data)

    def socket_listener(self, data):
        if data['type'] == MessageType.query_room_users_result:
            if data['parameters'][1] != self.target['id']:
                return
            # [id, nickname, online, username]
            self.user_list = data['parameters'][0]
            self.refresh_user_listbox()

        if data['type'] == MessageType.room_user_on_off_line:
            # [room_id, user_id, online]
            if data['parameters'][0] != self.target['id']:
                return
            for i in range(0, len(self.user_list)):
                if self.user_list[i][0] == data['parameters'][1]:
                    self.user_list[i][2] = data['parameters'][2]

            self.refresh_user_listbox()

    def refresh_user_listbox(self):
        # [id, nickname, online, username]
        self.user_listbox.delete(0, END)
        self.user_list.sort(key=lambda x: x[2])

        for user in self.user_list:
            self.user_listbox.insert(0, user[1] + ("(在线)" if user[2] else "(离线)"))
            self.user_listbox.itemconfig(0, {'fg': ("green" if user[2] else "#999")})

    # 历史消息显示
    def digest_message(self, data):
        time = datetime.datetime.fromtimestamp(
            int(data['time']) / 1000
        ).strftime('%Y-%m-%d %H:%M:%S')
        self.append_to_chat_box(data['sender_name'] + "  " + time + '\n',
                                [('me' if client.memory.current_user['id'] == data[
                                    'sender_id'] else 'them')])
        # type 0:文字消息 1:图片消息 4文件消息
        if data['message']['type'] == 0:
            self.tag_i += 1
            self.chat_box.tag_config('new' + str(self.tag_i),
                                     lmargin1=16,
                                     lmargin2=16,
                                     foreground=data['message']['fontcolor'],
                                     font=(None, data['message']['fontsize']))
            self.append_to_chat_box(data['message']['data'] + '\n',
                                    ['new' + str(self.tag_i)])
        if data['message']['type'] == 1:
            try:
                client.memory.tk_img_ref.append(ImageTk.PhotoImage(data=data['message']['data']))
                self.chat_box.image_create(END, image=client.memory.tk_img_ref[-1], padx=16, pady=5)
                self.append_to_chat_box('\n', [''])
            except PIL.UnidentifiedImageError as e:
                self.tag_i += 1
                self.chat_box.tag_config('new' + str(self.tag_i),
                                         lmargin1=16,
                                         lmargin2=16,
                                         foreground='red',
                                         font=(None, 20))
                self.append_to_chat_box('\n' + '<此图片已损坏>\n' + '\n',
                                        ['new' + str(self.tag_i)])
        if data['message']['type'] == 2:
            # print("111222")
            self.tag_i += 1
            self.chat_box.tag_config('new' + str(self.tag_i),
                                     lmargin1=16,
                                     lmargin2=16,
                                     foreground=data['message']['fontcolor'],
                                     font=(None, data['message']['fontsize']))
            self.append_to_chat_box(data['message']['data'] + '\n',
                                    'new' + str(self.tag_i))
        if data['message']['type'] == 3:
            self.tag_i += 1
            self.chat_box.tag_config('new' + str(self.tag_i),
                                     lmargin1=16,
                                     lmargin2=16,
                                     foreground=data['message']['fontcolor'],
                                     font=(None, data['message']['fontsize']))
            self.append_to_chat_box(data['message']['data'] + '\n',
                                    'new' + str(self.tag_i))
        if data['message']['type'] == 4:
            self.tag_i += 1
            self.chat_box.tag_config('new' + str(self.tag_i),
                                     lmargin1=16,
                                     lmargin2=16,
                                     foreground='blue',
                                     font=(None, 14))
            self.append_to_chat_box('[' + data['message']['filename'] + ']\n',
                                    ['new' + str(self.tag_i), 'file'])
            file.save_file(data['message']['data'], data['message']['filename'])

    # 接收消息
    def receiving_message(self, data):
        global clients
        global isCreated
        time = datetime.datetime.fromtimestamp(
            int(data['time']) / 1000
        ).strftime('%Y-%m-%d %H:%M:%S')
        self.append_to_chat_box(data['sender_name'] + "  " + time + '\n',
                                ('me' if client.memory.current_user['id'] == data[
                                    'sender_id'] else 'them'))
        print(data['message']['type'])
        # type 0 - 文字消息 1 - 图片消息 2 - 语音通话
        if data['message']['type'] == 0:
            self.tag_i += 1
            self.chat_box.tag_config('new' + str(self.tag_i),
                                     lmargin1=16,
                                     lmargin2=16,
                                     foreground=data['message']['fontcolor'],
                                     font=(None, data['message']['fontsize']))
            self.append_to_chat_box(data['message']['data'] + '\n',
                                    'new' + str(self.tag_i))
        if data['message']['type'] == 1:
            try:
                client.memory.tk_img_ref.append(ImageTk.PhotoImage(data=data['message']['data']))
                self.chat_box.image_create(END, image=client.memory.tk_img_ref[-1], padx=16, pady=5)
                self.append_to_chat_box('\n', [''])
            except PIL.UnidentifiedImageError as e:
                self.tag_i += 1
                self.chat_box.tag_config('new' + str(self.tag_i),
                                         lmargin1=16,
                                         lmargin2=16,
                                         foreground='red',
                                         font=(None, 20))
                self.append_to_chat_box('\n' + '<此图片已损坏>\n' + '\n',
                                        ['new' + str(self.tag_i)])
        if data['message']['type'] == 2:
            self.tag_i += 1
            self.chat_box.tag_config('new' + str(self.tag_i),
                                     lmargin1=16,
                                     lmargin2=16,
                                     foreground=data['message']['fontcolor'],
                                     font=(None, data['message']['fontsize']))
            self.append_to_chat_box(data['message']['data'] + '\n',
                                    'new' + str(self.tag_i))
            if not isCreated:
                self.thread2.start()
                print(2333)
        if data['message']['type'] == 3:
            self.tag_i += 1
            self.chat_box.tag_config('new' + str(self.tag_i),
                                     lmargin1=16,
                                     lmargin2=16,
                                     foreground=data['message']['fontcolor'],
                                     font=(None, data['message']['fontsize']))
            self.append_to_chat_box(data['message']['data'] + '\n',
                                    'new' + str(self.tag_i))
            print("语音通话结束")
            clients.stop()
        if data['message']['type'] == 4:
            self.tag_i += 1
            self.chat_box.tag_config('new' + str(self.tag_i),
                                     lmargin1=16,
                                     lmargin2=16,
                                     foreground='blue',
                                     font=(None, 14))
            self.append_to_chat_box('[' + data['message']['filename'] + ']\n',
                                    ['new' + str(self.tag_i), 'file'])
            file.save_file(data['message']['data'], data['message']['filename'])

    def user_listbox_double_click(self, _):
        if len(self.user_listbox.curselection()) == 0:
            return None
        index = self.user_listbox.curselection()[0]
        selected_user_id = self.user_list[len(self.user_list) - 1 - index][0]
        selected_user_nickname = self.user_list[len(self.user_list) - 1 - index][1]
        selected_user_username = self.user_list[len(self.user_list) - 1 - index][3]
        if selected_user_id == client.memory.current_user['id']:
            return
        client.memory.contact_window[0].try_open_user_id(selected_user_id, selected_user_nickname,
                                                         selected_user_username)
        # pprint(selected_user_id)
        return

    def VideoChat(self):
        vclient = Video_Client(IP, PORT, LEVEL, VERSION)
        vserver = Video_Server(PORT, VERSION)
        vclient.start()
        time.sleep(1)  # make delay to start server
        vserver.start()
        while True:
            time.sleep(1)
            if not vserver.isAlive() or not vclient.isAlive():
                print("Video connection lost...")
                sys.exit(0)

    def __init__(self, target, master=None):
        super().__init__(master)
        self.master = master
        self.target = target
        self.user_listbox = tk.Listbox(self, bg='#EEE')
        client.util.socket_listener.add_listener(self.socket_listener)
        client.memory.unread_message_count[self.target['type']][self.target['id']] = 0
        client.memory.contact_window[0].refresh_contacts()
        master.resizable(width=True, height=True)
        master.geometry('660x500')
        master.minsize(520, 370)
        master.protocol("WM_DELETE_WINDOW", self.remove_listener_and_close)
        self.sc = client.memory.sc

        if self.target['type'] == 0:
            self.master.title(self.target['nickname'])

        if self.target['type'] == 1:
            self.master.title("群:" + str(self.target['id']) + " " + self.target['room_name'])
            self.sc.send(MessageType.query_room_users, self.target['id'])

        self.right_frame = tk.Frame(self, bg='white')

        self.user_listbox.bind('<Double-Button-1>', self.user_listbox_double_click)
        if self.target['type'] == 1:
            self.user_listbox.pack(side=LEFT, expand=False, fill=BOTH)
        self.right_frame.pack(side=LEFT, expand=True, fill=BOTH)

        self.input_frame = tk.Frame(self.right_frame, bg='white')

        self.input_textbox = ScrolledText(self.right_frame, height=10)
        self.input_textbox.bind("<Control-Return>", self.send_message)
        self.input_textbox.bind_all('<Key>', self.apply_font_change)

        self.send_btn = tk.Button(self.input_frame, text='发送消息(Ctrl+Enter)', command=self.send_message,
                                  bg='Honeydew')
        self.send_btn.pack(side=RIGHT, expand=False)

        self.font_btn = tk.Button(self.input_frame, text='字体颜色', command=self.choose_color, bg='PaleVioletRed')
        self.font_btn.pack(side=LEFT, expand=False)

        self.font_btn = tk.Button(self.input_frame, text='字体大小', command=self.choose_font_size, bg='PowderBlue')
        self.font_btn.pack(side=LEFT, expand=False)

        self.image_btn = tk.Button(self.input_frame, text='发送图片', command=self.send_image, bg='LightCyan')
        self.image_btn.pack(side=LEFT, expand=False)

        self.file_btn = tk.Button(self.input_frame, text='发送文件', command=self.send_file)
        self.file_btn.pack(side=LEFT, expand=False)

        self.emoji_btn = tk.Button(self.input_frame, text='发送表情', command=self.create_image_label)
        self.emoji_btn.pack(side=LEFT, expand=False)

        # 语言通话测试
        self.phone_btn = tk.Button(self.input_frame, text='语音通话', command=self.phone_call, bg='OldLace')
        self.phone_btn.pack(side=LEFT, expand=False)

        self.start_btn = tk.Button(self.input_frame, text='视频通话', command=self.VideoChat)
        self.start_btn.pack(side=LEFT, expand=False)

        self.chat_box = ScrolledText(self.right_frame, bg='white')
        self.input_frame.pack(side=BOTTOM, fill=X, expand=False)
        self.input_textbox.pack(side=BOTTOM, fill=X, expand=False, padx=(0, 0), pady=(0, 0))
        self.chat_box.pack(side=BOTTOM, fill=BOTH, expand=True)
        self.chat_box.bind("<Key>", lambda e: "break")
        self.chat_box.tag_bind("file", "<Button-1>", self.file_clicked)
        self.chat_box.tag_config("file", foreground="blue", underline=True)
        self.chat_box.tag_config("default", lmargin1=10, lmargin2=10, rmargin=10)
        self.chat_box.tag_config("me", foreground="green", spacing1='5')
        self.chat_box.tag_config("them", foreground="blue", spacing1='5')
        self.chat_box.tag_config("message", foreground="black", spacing1='0')
        self.chat_box.tag_config("system", foreground="grey", spacing1='0',
                                 justify='center',
                                 font=(None, 8))

        self.pack(expand=True, fill=BOTH)

        add_message_listener(self.target['type'], self.target['id'], self.message_listener)
        master.protocol("WM_DELETE_WINDOW", self.remove_listener_and_close)

        # 历史消息显示
        if target['id'] in client.memory.chat_history[self.target['type']]:
            for msg in client.memory.chat_history[self.target['type']][target['id']]:
                self.digest_message(msg)

            self.append_to_chat_box('- 以上是历史消息 -\n', ['system'])

    def append_to_chat_box(self, message, tags):
        self.chat_box.insert(tk.END, message, tags)
        self.chat_box.update()
        self.chat_box.see(tk.END)

    def send_message(self, _=None):
        message = self.input_textbox.get("1.0", END)
        if not message or message.replace(" ", "").replace("\r", "").replace("\n", "") == '':
            return
        self.sc.send(MessageType.send_message,
                     {'target_type': self.target['type'], 'target_id': self.target['id'],
                      'message': {
                          'type': 0,
                          'data': message.strip().strip('\n'),
                          'fontsize': self.font_size,
                          'fontcolor': self.font_color
                      }
                      })
        self.input_textbox.delete("1.0", END)
        return 'break'

    def choose_color(self):
        _, self.font_color = colorchooser.askcolor(initialcolor=self.font_color)
        self.apply_font_change(None)

    def choose_font_size(self):
        result = simpledialog.askinteger("设置", "请输入字体大小", initialvalue=self.font_size)
        if result is None:
            return
        self.font_size = result
        self.apply_font_change(None)

    def apply_font_change(self, _):
        try:
            self.input_textbox.tag_config('new', foreground=self.font_color, font=(None, self.font_size))
            self.input_textbox.tag_add('new', '1.0', END)
        except:
            pass

    def send_image(self):
        filename = filedialog.askopenfilename(filetypes=[("Image Files",
                                                          ["*.jpg", "*.jpeg", "*.png", "*.gif", "*.JPG", "*.JPEG",
                                                           "*.PNG", "*.GIF"]),
                                                         ("All Files", ["*.*"])])
        if filename is None or filename == '':
            return
        with open(filename, "rb") as imageFile:
            f = imageFile.read()
            b = bytearray(f)

            self.sc.send(MessageType.send_message,
                         {'target_type': self.target['type'], 'target_id': self.target['id'],
                          'message': {'type': 1, 'data': b}})

    def send_file(self):
        filename = filedialog.askopenfilename(filetypes=[("All Files", ["*.*"])])
        if filename is None or filename == '':
            return
        with open(filename, "rb") as any_file:
            f = any_file.read()
            file_name = os.path.basename(filename)

            self.sc.send(MessageType.send_message,
                         {'target_type': self.target['type'], 'target_id': self.target['id'],
                          'message': {'type': 4, 'filename': file_name, 'data': f}})

    def file_clicked(self, event):
        tag_ranges = event.widget.tag_ranges("file")
        start_index, end_index = tag_ranges[0], tag_ranges[1]
        file_name = event.widget.get(start_index, end_index)
        file_name = file_name.strip('\n').strip('[').strip(']')
        file_path = filedialog.askdirectory()
        if file_path is None or file_path == '':
            return
        # 源文件路径
        source_file = tempfile.gettempdir() + sep + 'chatroom' + sep + 'files' + sep + file_name
        shutil.copy(source_file, file_path)
        print("文件保存：", file_name, " , ", file_path)

    # 定义发送函数
    def send_emoji(self, image_path):
        try:
            # 打开选中的图片文件
            with open(image_path, "rb") as file:
                image_data = file.read()
                b = bytearray(image_data)
            self.sc.send(MessageType.send_message,
                         {'target_type': self.target['type'], 'target_id': self.target['id'],
                          'message': {'type': 1, 'data': b}})
        except Exception as e:
            messagebox.showerror("发送失败", str(e))

    def create_image_label(self):
     if self.open == 0:
          self.open = 1
          for index in range(10):
            # 打开图片文件
            image_path = f"image/emoji/image_{index + 1}.png"  # 图片文件路径
            image = Image.open(image_path)

            # 创建缩略图
            image.thumbnail((10, 10))

            # 显示图片
            photo = ImageTk.PhotoImage(image)
            label = tk.Label(self.input_frame, image=photo)
            label.image = photo  # 保持对Image对象的引用，避免被垃圾回收
            label.pack(side=tk.LEFT)

            # 绑定鼠标点击事件，点击时发送选中的图片
            label.bind("<Button-1>", lambda event, path=image_path: self.send_emoji(path))

            # 添加到标签列表
            self.image_labels.append(label)
     else:
            self.open = 0
            for label in self.image_labels:
             label.pack_forget()





    # 语言通话测试
    def phone_call(self):
        if not self.isCalling:
            self.isCalling = True
            self.sc.send(MessageType.send_message,
                         {'target_type': self.target['type'], 'target_id': self.target['id'],
                          'message': {
                              'type': 2,
                              'data': '邀请您进行语音通话',
                              'fontsize': self.font_size,
                              'fontcolor': self.font_color
                          }
                          })
            self.phone_btn['text'] = '结束通话'
            self.phone_btn['command'] = self.phone_call_stop
            self.input_textbox.delete("1.0", END)
            self.thread1.start()
            self.thread2.start()
        return 'break'

    def phone_call_stop(self):
        if self.isCalling:
            self.isCalling = False
            self.sc.send(MessageType.send_message,
                         {'target_type': self.target['type'], 'target_id': self.target['id'],
                          'message': {
                              'type': 3,
                              'data': '语音通话结束',
                              'fontsize': self.font_size,
                              'fontcolor': self.font_color
                          }
                          })
            self.input_textbox.delete("1.0", END)
            # stop_thread(self.thread1)
            # stop_thread(self.thread2)
            # signal.pthread_kill(self.thread1.ident, signal.SIGTSTP)
            # signal.pthread_kill(self.thread2.ident, signal.SIGTSTP)
            global clients
            clients.stop()
            global isCreated
            isCreated = False
            global server
            server.stop()
            # stop_thread(self.thread1)
            # stop_thread(self.thread2)
            self.phone_btn['text'] = '语音通话'
            self.phone_btn['command'] = self.phone_call
        return 'break'
